#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
# @version: v1.0
# @author : cd
# @Email : 19688513@qq.com
# @Project : horizons-engine-pybroker
# @File : UltraMain.py
# @Software: PyCharm
# @time: 2025/6/27 11:47
# @description : 
"""

import urllib3
import logging
import time
import os
import pandas as pd
from datetime import date, datetime, timedelta
import traceback
from apscheduler.schedulers.background import BackgroundScheduler

from AKShareDataFetcher import AKShareDataFetcher
from Email.email_service import EmailService
from ExcelExporter import ExcelExporter
from SignalGenerator import SignalGenerator
from StockBasicInfoManager import StockBasicInfoManager
from StockSignalsManager import StockSignalsManager
from TradeDateManager import TradeDateManager
from YearlyStockDataManager import YearlyStockDataManager  # 使用YearlyStockDataManager

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# 禁用不安全的请求警告
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

# 初始化全局组件
email_service = EmailService()
trade_manager = TradeDateManager()
# 创建Excel导出器实例
exporter = ExcelExporter()

# 初始化调度器
scheduler = BackgroundScheduler()

# 股票基本信息数据库路径
STOCK_BASIC_DB_PATH = "data/stock_basic_info.db"
# 股票年度数据数据库路径
STOCK_YEAR_DB_PATH = "data/stock_year_data.db"


def send_error_report(error_message, error_details):
    error_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
    subject = f"系统错误报告 - {error_time}"

    # 发送错误报告
    error_sent = email_service.send_error_report(
        subject=subject,
        email_title="系统监控",
        error_message=error_message,
        error_details=error_details,
    )
    logging.info(f"错误报告发送{'成功' if error_sent else '失败'}")


def fetch_TradeDate():
    try:
        logging.info("开始获取交易日期...")
        trade_manager.initialize_trade_dates()
        logging.info("交易日期获取完成。")
    except Exception as e:
        # 获取详细的错误信息（包括行号、方法名等）
        error_detail = traceback.format_exc()
        logging.error(f"获取交易日期时发生错误:\n{error_detail}")
        send_error_report(error_detail, "fetch_TradeDate")


def fetch_StockBasicInfo():
    try:
        logging.info("开始获取股票基本信息...")
        # 创建股票基本信息管理实例
        stock_manager = StockBasicInfoManager(max_workers=8)
        # 初始化股票数据库
        stock_manager.initialize_stock_database()
        logging.info("股票基本信息初始化成功")
    except Exception as e:
        # 获取详细的错误信息（包括行号、方法名等）
        error_detail = traceback.format_exc()
        logging.error(f"获取股票基本信息时发生错误:\n{error_detail}")
        send_error_report(error_detail, "fetch_StockBasicInfo")


def fetch_UltraOptimizedStockDataInfo():
    try:
        logging.info("开始获取年度数据...")

        # 使用 YearlyStockDataManager
        data_manager = YearlyStockDataManager(
            db_path=STOCK_YEAR_DB_PATH,
            batch_size=200,
            cache_size_mb=512
        )
        trade_calendar = TradeDateManager()
        signal_generator = SignalGenerator()
        stock_manager = StockBasicInfoManager(
            max_workers=12
        )
        signal_manager = StockSignalsManager(
            trade_calendar=trade_calendar,
            data_manager=data_manager,
            signal_generator=signal_generator,
            stock_manager = stock_manager
        )

        # 运行前检查缓存一致性
        if not data_manager.check_cache_consistency():
            logging.warning("缓存不一致，重置缓存")
            data_manager.metadata_cache = {}
            data_manager.load_metadata_cache()

        # 获取当前日期和时间
        now = datetime.now()
        current_date = now.date()

        # 如果是下午3点前，使用前一天的数据（当日数据尚未完全生成）
        if now.hour < 15:
            target_date = current_date - timedelta(days=1)
            logging.info(f"当前时间在15点前，使用前一天日期: {target_date}")
        else:
            target_date = current_date
            logging.info(f"当前时间在15点后，使用当天日期: {target_date}")

        # 检查是否为交易日
        if not trade_manager.is_trade_day(target_date):
            logging.info(f"{target_date} 不是交易日，跳过数据获取。")
            return

        # 增量更新数据（获取最近5天的数据）
        data_manager.fetch_combined_data()

        # 生成信号
        date_str = target_date.strftime('%Y%m%d')

        signals = signal_manager.fetch_and_generate_signals(date_str)

        # 生成信号报告
        report_path = exporter.export_signals_report(signals, target_date)

        logging.info(f"信号报告已保存至: {report_path}")

        # 动态生成信号描述内容
        # 获取信号映射和描述
        signal_map = signal_generator.SIGNAL_MAP
        signal_descriptions = signal_generator.SIGNAL_DESCRIPTIONS

        # 创建反向映射：编号 -> 信号名称
        reverse_signal_map = {v: k for k, v in signal_map.items()}

        # 创建信号描述HTML
        signals_html = []

        # 统一信号ID范围 (1-23)
        for signal_id in range(1, 24):  # range(1, 24) 包含1-23
            # 获取信号名称
            signal_name = reverse_signal_map.get(signal_id, f"信号{signal_id}")

            # 获取信号描述，如果不存在则跳过
            description = signal_descriptions.get(signal_id)

            # 如果描述存在，则添加到HTML列表中
            if description:
                signals_html.append(f"{signal_name}: {signal_id}: {description}")

        # 准备邮件内容
        html_content = f"""
                    <h1>每日推荐 - {date_str}</h1>
                    <p>
                        <h3>信号说明:</h3>
                        {"<br>".join(signals_html)}
                    </p>
                """

        # 发送每日推荐
        recommendation_sent = email_service.send_daily_recommendation(
            html_content=html_content,
            email_title="每日推荐服务",
            subject=f"每日股票信号报告 - {date_str}",
            delay_seconds=5,  # 每组之间等待5秒
            attachment_path=report_path  # 附件路径
        )
        logging.info(f"每日推荐发送{'成功' if recommendation_sent else '部分失败'}")

    except Exception as e:
        # 获取详细的错误信息（包括行号、方法名等）
        error_detail = traceback.format_exc()
        logging.error(f"获取年度数据或发送邮件时发生错误:\n{error_detail}")
        send_error_report(error_detail, "fetch_UltraOptimizedStockDataInfo")


# 添加调度任务
scheduler.add_job(fetch_TradeDate, 'cron', hour=22, minute=1, id='fetch_trade_date')
scheduler.add_job(fetch_StockBasicInfo, 'cron', hour=22, minute=30, id='fetch_stock_basic_info')
scheduler.add_job(fetch_UltraOptimizedStockDataInfo, 'cron', hour=17, minute=0, id='fetch_stock_year_data')

if __name__ == "__main__":
    logging.info("启动任务调度器...")

    # 初始化必要数据
    try:
        # 1. 获取交易日期
        fetch_TradeDate()
        trade_manager.load_trade_dates()  # 确保交易日历加载到内存
        logging.info("交易日历已加载")

        # 2. 检查股票基本信息数据库是否存在
        stock_basic_db_exists = os.path.exists(STOCK_BASIC_DB_PATH) and os.path.getsize(STOCK_BASIC_DB_PATH) > 0

        if not stock_basic_db_exists:
            logging.info("股票基本信息数据库不存在或为空，执行初始化...")
            fetch_StockBasicInfo()
        else:
            logging.info("股票基本信息数据库已存在，跳过初始化")

        # fetch_UltraOptimizedStockDataInfo()

    except Exception as e:
        # 获取详细的错误信息（包括行号、方法名等）
        error_detail = traceback.format_exc()
        logging.error(f"初始化过程中发生错误:\n{error_detail}")
        send_error_report(error_detail, "initialization")

    scheduler.start()
    logging.info("任务调度器已启动")

    try:
        while True:
            time.sleep(3600)  # 每小时检查一次，减少CPU负载
    except (KeyboardInterrupt, SystemExit):
        logging.info("停止任务调度器...")
        scheduler.shutdown()
        logging.info("任务调度器已停止")